/* //device/system/broncho-ril/reference-ril.c
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <telephony/ril.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <alloca.h>
#include "atchannel.h"
#include "at_tok.h"
#include "ril_util.h"
#include "broncho-ril.h"
#include "ril-cc.h"

#define LOG_TAG "RIL"
#include "broncho_log.h"

typedef struct _CcContext
{
	RIL_LastCallFailCause 	failCase;
	int exist_call;
	int audio_mode;
	int call_volume;
}CcContext;

static const struct timeval TIMEVAL_0 = {0,0};

static CcContext* ccGetContext(RIL* thiz)
{
	CcContext* ctx = RIL_GetPrivInfo(thiz, "cc_ctx");
	if(ctx == NULL)
	{
		ctx = calloc(1, sizeof(CcContext));
		RIL_SetPrivInfo(thiz, ctx, "cc_ctx");
	}

	return ctx;
}

int RIL_ExistCalls(RIL* thiz)
{
	CcContext* ctx = RIL_GetPrivInfo(thiz, "cc_ctx");

	return  ctx->exist_call;
}

//broncho start add by lyl
static void skipNextComma(char **p_cur)
{
	if (*p_cur == NULL) return;

	while (**p_cur != '\0' && **p_cur != ',')
	{
		(*p_cur)++;
	}

	if (**p_cur == ',')
	{
		(*p_cur)++;
	}
}

static int cendCauseToCallFailCause(int cause)
{
	switch(cause)
	{
		case 17: return  CALL_FAIL_BUSY;
		case 34: return  CALL_FAIL_CONGESTION;
		default: return  CALL_FAIL_NORMAL;
	}
}

static RIL_LastCallFailCause ccStrToFailCase(const char* line)
{
	RIL_LastCallFailCause ret;
	int err;
	int cause;
	int type;

	//^CEND: call_x, reserve, end_status, cc_cause, call_type
	skipNextComma(&line);
	skipNextComma(&line);
	skipNextComma(&line);

	if(at_tok_hasmore(&line))
	{
		err = at_tok_nextint(&line, &cause);
		if (err < 0) return CALL_FAIL_NORMAL;
	}

	//call_type: (voice call : 0) ,(data network : 2)
	if(at_tok_hasmore(&line))
	{
		err = at_tok_nextint(&line, &type);
		return CALL_FAIL_NORMAL;
	}

	ret = cendCauseToCallFailCause(cause);

	return ret;
}
//broncho end add by lyl

static void ccHandleCallResult(RIL* thiz, const char* line)
{
	CcContext* ctx = ccGetContext(thiz);

	ctx->failCase = ccStrToFailCase(line);

	return;
}

static int clccStateToRILState(int state, RIL_CallState *p_state)

{
    switch(state) {
        case 0: *p_state = RIL_CALL_ACTIVE;   return 0;
        case 1: *p_state = RIL_CALL_HOLDING;  return 0;
        case 2: *p_state = RIL_CALL_DIALING;  return 0;
        case 3: *p_state = RIL_CALL_ALERTING; return 0;
        case 4: *p_state = RIL_CALL_INCOMING; return 0;
        case 5: *p_state = RIL_CALL_WAITING;  return 0;
        default: return -1;
    }
}

/**
 * Note: directly modified line and has *p_call point directly into
 * modified line
 */
static int callFromCLCCLine(char *line, RIL_Call *p_call)
{
        //+CLCC: 1,0,2,0,0,\"+18005551212\",145
        //     index,isMT,state,mode,isMpty(,number,TOA)?

    int err;
    int state;
    int mode;

    err = at_tok_start(&line);
    if (err < 0) goto error;

    err = at_tok_nextint(&line, &(p_call->index));
    if (err < 0) goto error;

    err = at_tok_nextbool(&line, &(p_call->isMT));
    if (err < 0) goto error;

    err = at_tok_nextint(&line, &state);
    if (err < 0) goto error;

    err = clccStateToRILState(state, &(p_call->state));
    if (err < 0) goto error;

    err = at_tok_nextint(&line, &mode);
    if (err < 0) goto error;

    p_call->isVoice = (mode == 0);

    err = at_tok_nextbool(&line, &(p_call->isMpty));
    if (err < 0) goto error;

    if (at_tok_hasmore(&line)) {
        err = at_tok_nextstr(&line, &(p_call->number));

        /* tolerate null here */
        if (err < 0) return 0;

        // Some lame implementations return strings
        // like "NOT AVAILABLE" in the CLCC line
        if (p_call->number != NULL
            && 0 == strspn(p_call->number, "+0123456789")
        ) {
            p_call->number = NULL;
        }

        err = at_tok_nextint(&line, &p_call->toa);
        if (err < 0) goto error;
    }

    return 0;

error:
    ALOGE("invalid CLCC line\n");
    return -1;
}

static void requestGetCurrentCalls(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
    int err = 0;
    ATResponse *p_response = NULL;
    ATLine *p_cur = NULL;
    int countCalls = 0;
    int countValidCalls = 0;
    RIL_Call *p_calls = NULL;
    RIL_Call **pp_calls = NULL;
    int i = 0;
    int needRepoll = 0;
	CcContext* ctx = ccGetContext(thiz);

    err = ril_send_command_multiline (thiz, "AT+CLCC", "+CLCC:", &p_response);
    if (err != 0 || p_response->success == 0) {
        RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
        return;
    }

    /* count the calls */
    for (countCalls = 0, p_cur = p_response->p_intermediates
            ; p_cur != NULL
            ; p_cur = p_cur->p_next
    ) {
		countCalls++;
    }

    /* yes, there's an array of pointers and then an array of structures */

    pp_calls = (RIL_Call **)alloca(countCalls * sizeof(RIL_Call *));
    p_calls = (RIL_Call *)alloca(countCalls * sizeof(RIL_Call));
    memset (p_calls, 0, countCalls * sizeof(RIL_Call));

    /* init the pointer array */
    for(i = 0; i < countCalls ; i++) {
        pp_calls[i] = &(p_calls[i]);
    }

    for (countValidCalls = 0, p_cur = p_response->p_intermediates
            ; p_cur != NULL
            ; p_cur = p_cur->p_next
    ) {
        err = callFromCLCCLine(p_cur->line, p_calls + countValidCalls);

        if (err != 0) {
            continue;
        }

        if (p_calls[countValidCalls].state != RIL_CALL_ACTIVE
            && p_calls[countValidCalls].state != RIL_CALL_HOLDING
        ) {
            needRepoll = 1;
        }

        countValidCalls++;
    }

	if(countValidCalls > 0) {
		ctx->exist_call = 1;
	} else {
		ctx->exist_call = 0;
	}

    RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, pp_calls,
            countValidCalls * sizeof (RIL_Call *));
    at_response_free(p_response);

	return;
}

static void requestDial(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
    RIL_Dial *p_dial;
    char cmd[64] = {0};
    int ret;
	ATResponse* p_resp = NULL;

    p_dial = (RIL_Dial *)data;
    snprintf(&cmd, 64, "ATD%s;", p_dial->address);
	ret = at_send_command_no_resp(RIL_GetAtChannel(thiz), cmd);

    /*sleep(1);
    ret = at_send_command_no_resp(RIL_GetAtChannel(thiz), "AT+SSAM=5");
    sleep(1);
    ret = at_send_command_no_resp(RIL_GetAtChannel(thiz), "AT+SSAM=0");*/

    //ret = ril_send_command(thiz, cmd, &p_resp);

	//ccHandleCallResult(thiz, p_resp->finalResponse);

    /* success or failure is ignored by the upper layer here.
       it will call GET_CURRENT_CALLS and determine success that way */
    RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
}

static void requestDtmfStart(RIL* thiz, void* data, size_t datalen, RIL_Token t)
{
	char c = ((char*)data)[0];
	char cmd[32] = {0};

	snprintf(cmd, 32, "AT+VTS=%c", c);
	if(ril_command_exec_for_result(thiz, cmd))
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}

	return;
}

static void requestLastCallFailCause(RIL* thiz, void* data, size_t datalen, RIL_Token t)
{
	CcContext* ctx = ccGetContext(thiz);
	int failCase = ctx->failCase;
  
	ALOGD("%%%%%%%%%%%%%fail:%d%%%%%%%%%%%%%%%%", failCase);
	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, &failCase, sizeof(failCase));

	return;
}

static void requestDtmfStop(RIL* thiz, void* data, size_t datalen, RIL_Token t)
{
	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	/*
	if(ril_command_exec_for_result(thiz, "AT"))
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}
	*/

	return;
}

static void requestDtmf(RIL* thiz, void* data, size_t datalen, RIL_Token t)
{
	char c = ((char*)data)[0];
	char cmd[32] = {0};

	snprintf(cmd, 32, "AT+VTS=%c", c);
	if(ril_command_exec_for_result(thiz, cmd))
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}

	return;
}

static void requestSeparateConnection(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
    char  cmd[12] = {0};
    int   party = ((int*)data)[0];

    // Make sure that party is in a valid range.
    // (Note: The Telephony middle layer imposes a range of 1 to 7.
    // It's sufficient for us to just make sure it's single digit.)
    if (party > 0 && party < 10) 
	{
        sprintf(cmd, "AT+CHLD=2%d", party);
        ril_send_command(thiz, cmd, NULL);
        RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
    } 
	else 
	{
        RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
    }

	return;
}

static void requestUDUB(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	 ril_send_command(thiz, "ATH", NULL);
	 RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);

	 return;
}

static void requestConference(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	ril_send_command(thiz, "AT+CHLD=3", NULL);
	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);

	return;
}

static void requestAnswer(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	ril_send_command(thiz, "ATA", NULL);
	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);

	return;
}

static void requestSwitchWaitingOrHoldingAndActive(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	//broncho add by longyanliang
	ATResponse *p_response = NULL;
	int err = 0;
	
	//err = ril_send_command(thiz, "AT+CHLD=2",  &p_response);
	err = ril_send_command_full(thiz, "AT+CHLD=2", 15000, &p_response);

	if(err < 0 || p_response->success == 0)
	{
	   RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	   at_response_free(p_response);
	   return ;
	}

	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	at_response_free(p_response);

	return;
}

static void requestHangupForegroundRresumeBackground(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	ril_send_command(thiz, "AT+CHLD=1", NULL);

	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);

	return;
}

static void requestHangupWaitingOrBackground(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{	
	int err = 0;
    ATResponse *p_response = NULL;
    ATLine *p_cur = NULL;
    int countCalls = 0;

    err = ril_send_command_multiline (thiz, "AT+CLCC", "+CLCC:", &p_response);
    if (err == 0 && p_response->success == 1) {
		/* count the calls */
	    for (countCalls = 0, p_cur = p_response->p_intermediates
            ; p_cur != NULL
            ; p_cur = p_cur->p_next ) 
		{
	        countCalls++;
    	}
    }

    if(countCalls > 1)
    {
    	ril_send_command(thiz, "AT+CHLD=0", NULL);
    } else if(countCalls == 1)
    {
		char cmd[32] = {0};
		int index = 1;		
		at_line_scanf_int(p_response->p_intermediates->line, &index);

		//broncho modify by lyl
		//snprintf(cmd, 32, "AT+CHLD=1%d", index);
		//ril_send_command(thiz, cmd, NULL);
		ril_send_command(thiz, "AT+CHLD=0", NULL);
	}
	at_response_free(p_response);
	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	return;
}

static void requestHangup(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
    int *p_line;
    int ret;
    char cmd[32] = {0};

    p_line = (int *)data;

    // 3GPP 22.030 6.5.5
    // "Releases a specific active call X"
    //broncho modify by lyl
	/*if(RIL_GetState(thiz) == RADIO_STATE_SIM_READY) {
        snprintf(cmd, 32, "AT+CHLD=1%d", p_line[0]);
	} else {
		snprintf(cmd, 32, "ATH");
	}*/
	snprintf(cmd, 32, "ATH");
    
	ret = ril_send_command(thiz, cmd, NULL);
    /* success or failure is ignored by the upper layer here.
       it will call GET_CURRENT_CALLS and determine success that way */
    RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
}

/**
 * Called by atchannel when an unsolicited line appears
 * This is called on atchannel's reader thread. AT commands may
 * not be issued here
 */
int RIL_initialize_cc (RIL* thiz)
{
	/*  Alternating voice/data off */
    //ril_send_command(thiz, "AT+CMOD=0", NULL);

	ril_send_command(thiz, "AT+CLIP=1", NULL);
	ril_send_command(thiz, "AT+VGR=9", NULL);

	//wupeng test, use earphone.
	//ril_send_command(thiz, "AT+SSAM=2", NULL);

	return 1;
}

static void onUnsolicited_Ring(RIL* thiz, const char* s)
{
	RIL_onUnsolicitedResponse(thiz,
			RIL_UNSOL_CALL_RING, NULL, 0);
	RIL_onUnsolicitedResponse (thiz,
			RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
			NULL, 0);
	return;
}

int RIL_onUnsolicited_cc (RIL* thiz, const char* s)
{
	int ret = 0;

	if (strStartsWith(s,"RING"))
	{
		onUnsolicited_Ring(thiz, s);
		ret = 1;
	}
	else if (strStartsWith(s, "+CCWA")
			|| strStartsWith(s, "+CLIP")
			|| strStartsWith(s, "^DSCI")
			|| strStartsWith(s, "+SIND: 2")
			|| strStartsWith(s, "+SIND: 5")
			|| strStartsWith(s, "+SIND: 6")
			|| strStartsWith(s, "+SIND: 9"))
	{
		RIL_onUnsolicitedResponse (thiz,
				RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
				NULL, 0);
		ret = 1;
	}
	else if(/*strStartsWith(s, "NO CARRIER")
			  || strStartsWith(s, "NO ANSWER")
			  || strStartsWith(s, "NO DIALTONE")*/
			strStartsWith(s, "^CEND") )
	{
		ccHandleCallResult(thiz, s);
		ret = 0;
	}
	else
	{
		ret = 0;
	}

	return ret;
}

/* broncho yinlijun add for A13 ril interface 20121029*/
static void srsRequestSetCallClockSync(RIL* thiz, int fd, struct srs_message *message)
{
	// not implemented
	return;
}

//broncho add by longyanliang 2013-06-29
static void set_call_volume(RIL * thiz)
{
    char cmd[32] = {0};
    CcContext* ctx = ccGetContext(thiz);
	snprintf(&cmd, 32, "AT+VGR=%d", ctx->call_volume);
    ril_send_command(thiz, cmd, NULL);
}

static void srsRequestSetCallVolume(RIL* thiz, int fd, struct srs_message *message)
{
	struct srs_snd_call_volume *call_volume = (struct srs_snd_call_volume *) message->data;
	ALOGD("Call volume for: 0x%x vol = 0x%x\n", call_volume->type, call_volume->volume);
    CcContext* ctx = ccGetContext(thiz);
    ctx->call_volume = call_volume->volume + 2;
    RIL_requestTimedCallback(thiz, set_call_volume, thiz, &TIMEVAL_0);

	return;
}

static void srsRequestSetCallAudioPath(RIL* thiz, int fd, struct srs_message *message)
{
	// not implemented
	return;
}

//broncho add by longyanliang 2013-01-09
static void set_audio_mode(RIL* thiz)
{
    char cmd[32] = {0};
    CcContext* ctx = ccGetContext(thiz);
    snprintf(&cmd, 32, "AT+SSAM=%d", ctx->audio_mode);
    ril_send_command(thiz, cmd, NULL);
}

static void srsRequestSetBtAudioMode(RIL* thiz, int fd, struct srs_message *message)
{
    struct srs_snd_bt_mode *bt_mode = (struct srs_snd_bt_mode *)message->data;
    CcContext* ctx = ccGetContext(thiz);
	
	if(bt_mode->mode == 4)
	{
        ctx->audio_mode = 5;
	}else
	{
        ctx->audio_mode = 0;
	}

    RIL_requestTimedCallback(thiz, set_audio_mode, thiz, &TIMEVAL_0);
    
    return;
}

int RIL_onSrsRequest_cc(RIL* thiz, int fd, struct srs_message *message) 
{
	switch(message->command) 
	{
		case SRS_SND_SET_CALL_CLOCK_SYNC:
			srsRequestSetCallClockSync(thiz, fd, message);
			break;
		case SRS_SND_SET_CALL_VOLUME:
			srsRequestSetCallVolume(thiz, fd, message);
			break;
		case SRS_SND_SET_CALL_AUDIO_PATH:
			srsRequestSetCallAudioPath(thiz, fd, message);
			break;
        
        //broncho add by longyanliang 2012-01-09
		case SRS_SND_SET_BT_AUDIO_MODE:
			srsRequestSetBtAudioMode(thiz, fd, message);
			break;

		default:
			ALOGD("Unhandled command: (%04x)", message->command);
			break;
	}

	return 1;
}
/* broncho yinlijun add end*/

int RIL_onRequest_cc (RIL* thiz, int request, void *data, size_t datalen, RIL_Token t)
{
	switch (request)
	{
		case RIL_REQUEST_GET_CURRENT_CALLS:
			requestGetCurrentCalls(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_DIAL:
			requestDial(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_HANGUP:
			requestHangup(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
			requestHangupWaitingOrBackground(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
			requestHangupForegroundRresumeBackground(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
			requestSwitchWaitingOrHoldingAndActive(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_ANSWER:
			requestAnswer(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_CONFERENCE:
			requestConference(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_UDUB:
			requestUDUB(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_SEPARATE_CONNECTION:
			requestSeparateConnection(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_DTMF:
			requestDtmf(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_DTMF_START:
			requestDtmfStart(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_DTMF_STOP:
			requestDtmfStop(thiz, data, datalen, t);
			break;

		case RIL_REQUEST_LAST_CALL_FAIL_CAUSE:
			requestLastCallFailCause(thiz, data, datalen, t);
			break;

		default:
			RIL_onRequestComplete(thiz, t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
			break;
	}

	return 1;
}

